home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / Level 0 Macintosh 07Aug94 / MyMalloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  23.0 KB  |  615 lines  |  [TEXT/KAHL]

  1. /* MyMalloc.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <Memory.h>
  26. #include <Errors.h>
  27. #include <Files.h>
  28. #pragma options(!pack_enums)
  29.  
  30. #include "MyMalloc.h"
  31.  
  32.  
  33. /* limitations: */
  34. /*  - total address space hasn't been tested for more than 24 bits. */
  35. /*  - blocks can't be more than 2^BOUNDARY - sizeof(BlockRec) blocks large */
  36. /*  - only works with flat addressing */
  37.  
  38.  
  39. /* alignment MUST be power of 2.  this cleverly aligns to long integers or */
  40. /* pointers, whichever is larger */
  41. #define ALIGNMENT ((sizeof(long) > sizeof(void*)) ? sizeof(long) : sizeof(void*))
  42.  
  43. /* provide 4 bits of correction (so that requested block sizes can be up to */
  44. /* 15 bytes smaller than the actual heap block allocated.  Hopefully this will work */
  45. /* even if unsigned longs are 64 bits. */
  46. #define BOUNDARY ((8 * sizeof(unsigned long)) - 4)
  47.  
  48. /* this macro calculates the largest possible size a user could request and */
  49. /* not break the allocator by finding out what the largest block size is and */
  50. /* subtracting the header to find out the content size.  Actually, this will be */
  51. /* a bit smaller, since not of all the header is actually present when the */
  52. /* block is allocated (there is some overlap between header and data area) */
  53. #define MEMBLOCKLIMIT ((1UL << BOUNDARY) - sizeof(BlockRec))
  54.  
  55. /* this defines the smallest amount of memory that will be requested from the */
  56. /* system if there is not enough already in the heap to satisfy a request. */
  57. /* (for debugging purposes, we make it ridiculously small.)  Make sure that */
  58. /* this is an aligned value!  see notes about exo-core below in the code. */
  59. #if DEBUG
  60.     #define MINMORECORESIZE (16L)
  61.     #define MINEXOCORESIZE (16L)
  62. #else
  63.     #define MINMORECORESIZE (4096L)
  64.     #define MINEXOCORESIZE (262144L)
  65. #endif
  66.  
  67. /* amount of space reserved below our first handle for system objects */
  68. #define LOWMEMRESERVEDSIZE (49152L)
  69.  
  70. /* when the local heap runs out of space, we try to allocate as much as possible from */
  71. /* it so that we don't waste that space.  this is done in increments of the following */
  72. #define LOCALINCREMENTSIZE (4096L)
  73.  
  74. /* encode the total size of a block and the number of bytes less the requested */
  75. /* size actually was into a single unsigned long word, so that we don't have */
  76. /* to waste any more bytes than we need.  This is done by remembering the total */
  77. /* allocated size and the number of bytes more than the requested size that the */
  78. /* total size was. */
  79. #define SIZEENCODE(size,correction) (((correction) << BOUNDARY) | (size))
  80.  
  81. /* find out what size the user requested by subtracting the amount of extra */
  82. /* bytes from the total size of the block */
  83. #define DECODEREQUESTEDSIZE(composite) (((composite) & ((1UL << BOUNDARY) - 1))\
  84.                     - (((composite) >> BOUNDARY) & 0x0f))
  85.  
  86. /* find out what the total size allocated was */
  87. #define DECODETOTALSIZE(composite) ((composite) & ((1UL << BOUNDARY) - 1))
  88.  
  89.  
  90. /* this structure defines the fields for a block header */
  91. typedef struct BlockRec
  92.     {
  93.         /* BlockSize contains the size of a block, which has differing meanings depending */
  94.         /* on whether the block is free or used.  If the block is free, then it is simply */
  95.         /* the total number of bytes in the block.  If the block is used, then it is */
  96.         /* an encoded size, with the block's total size & the correction for determining */
  97.         /* how much was requested when the block was allocated. */
  98.         unsigned long                BlockSize;
  99.         /* pointer to Next block in the free list is only used when block is free.  If */
  100.         /* block is allocated, then the data starts at the first byte of Next. */
  101.         /* That last bit is important because it allows us to adjust the number and */
  102.         /* size of header fields before the Next for debugging purposes. */
  103.         struct BlockRec*        Next;
  104.     } BlockRec;
  105.  
  106.  
  107. /* list of blocks that are free, linked through the Next field */
  108. static BlockRec*            FreeList = NIL;
  109.  
  110. /* last macintosh handle allocated.  when we need morecore, we try to extend this */
  111. /* before allocating another handle so that we can keep all storage contiguous. */
  112. static char**                    LastHandleAllocated = NIL;
  113.  
  114. /* this flag is True if LastHandleAllocated is local to the heap, or False if */
  115. /* it is allocated outside of our heap. */
  116. static MyBoolean            LastHandleIsLocal;
  117.  
  118. /* debugging variable for detecting memory leaks and multiple releases. */
  119. EXECUTE(static long        AllocationCount = 0;)
  120.  
  121.  
  122. /* initialize the memory allocator. */
  123. MyBoolean        InitializeMyMalloc(void)
  124.     {
  125.         char**        Temp;
  126.  
  127.         /* since we stick a handle in the heap and lock it, but try to resize it, */
  128.         /* we want to reserve some space below it for static objects. */
  129.         ReserveMem(LOWMEMRESERVEDSIZE);
  130.         Temp = NewHandle(LOWMEMRESERVEDSIZE);
  131.         if (Temp == NIL)
  132.             {
  133.                 return False;
  134.             }
  135.         HLock(Temp);
  136.         /* now allocate an initial handle for our heap thing */
  137.         LastHandleAllocated = NewHandle(0);
  138.         if (LastHandleAllocated == NIL)
  139.             {
  140.                 return False;
  141.             }
  142.         HLock(LastHandleAllocated);
  143.         LastHandleIsLocal = True;
  144.         /* finally, dispose of the low memory space to free it up for the system */
  145.         DisposeHandle(Temp);
  146.         return True;
  147.     }
  148.  
  149.  
  150. /* allocate a new block of memory.  If there is no free block large enough, the */
  151. /* heap is extended.  If the block size is greater than MINMORECORESIZE, then */
  152. /* a system block the size of the block will be allocated, otherwise a system */
  153. /* block of MINMORECORESIZE bytes will be allocated. */
  154. void*                BlockNew(long RequestedBlockSize)
  155.     {
  156.         BlockRec*                        Scan;
  157.         BlockRec*                        Lag;
  158.         unsigned long                Extra;
  159.         unsigned long                TotalSize;
  160.         void*                                ReturnValue;
  161.         long                                OSRequestedSize;
  162.         char**                            NewMemHandle;
  163.         MyBoolean                        NewMemHandleIsLocal;
  164.  
  165.         APRINT(("+BlockNew %l",RequestedBlockSize));
  166.         ERROR((RequestedBlockSize < 0) || (RequestedBlockSize > MEMBLOCKLIMIT),
  167.             PRERR(ForceAbort,"BlockNew:  allocation block size is out of range"));
  168.      TryAgainPoint:
  169.         Scan = FreeList;
  170.         Lag = NIL;
  171.         /* (BlockSize + Extra) % ALIGNMENT must == 0 */
  172.         Extra = (ALIGNMENT - 1) - ((RequestedBlockSize + 3) & (ALIGNMENT - 1));
  173.         /* figure out amount of extra space to add to request to get the actual */
  174.         /* block size we need to allocate.  The funny bit on the end figures out */
  175.         /* how many bytes are in the USED block's header.  (the free block's header */
  176.         /* has sizeof(BlockRec) bytes in it, but we can't use this since we overwrite */
  177.         /* the Next field, thus making the used header smaller.) */
  178.         TotalSize = RequestedBlockSize + Extra
  179.             + (long)((char*)&(((BlockRec*)NIL)->Next) - (char*)NIL);
  180.         if (TotalSize < sizeof(BlockRec))
  181.             {
  182.                 /* for tiny blocks, we must be sure we can always free the block */
  183.                 TotalSize = sizeof(BlockRec);
  184.             }
  185.         /* scan through list of free blocks to find one that's big enough */
  186.         while (Scan != NIL)
  187.             {
  188.                 if (Scan->BlockSize >= TotalSize)
  189.                     {
  190.                         /* found a big enough block */
  191.                         if (Scan->BlockSize < TotalSize + sizeof(BlockRec))
  192.                             {
  193.                                 /* block is too small to split -- take the whole thing */
  194.                                 /* eliminate this block from the free list */
  195.                                 if (Lag != NIL)
  196.                                     {
  197.                                         Lag->Next = Scan->Next;
  198.                                     }
  199.                                  else
  200.                                     {
  201.                                         FreeList = Scan->Next;
  202.                                     }
  203.                                 /* adjust block size of Scan */
  204.                                 /* since we are allocating the whole block, we use [preserve] the */
  205.                                 /* block's original size (see below) */
  206.                                 ERROR(DECODETOTALSIZE((unsigned long)SIZEENCODE(Scan->BlockSize,
  207.                                     Scan->BlockSize - RequestedBlockSize))
  208.                                     - DECODEREQUESTEDSIZE((unsigned long)SIZEENCODE(Scan->BlockSize,
  209.                                     Scan->BlockSize - RequestedBlockSize))
  210.                                     != Scan->BlockSize - RequestedBlockSize,PRERR(ForceAbort,
  211.                                     "BlockNew:  not enough bits specified for size correction"));
  212.                                 Scan->BlockSize = SIZEENCODE(Scan->BlockSize,
  213.                                     Scan->BlockSize - RequestedBlockSize);
  214.                                 /* set up return value */
  215.                                 ReturnValue = (void*)&(Scan->Next);
  216.                             }
  217.                          else
  218.                             {
  219.                                 /* block is big enough to be split */
  220.                                 /* first, make a new block header where the second part of the */
  221.                                 /* block starts, and set up the fields in the header */
  222.                                 if (Lag != NIL)
  223.                                     {
  224.                                         Lag->Next = (BlockRec*)((char*)Scan + TotalSize);
  225.                                         /* pointer update first! (else for 0 length blocks, it will */
  226.                                         /* get overwritten) */
  227.                                         Lag->Next->Next = Scan->Next;
  228.                                         Lag->Next->BlockSize = Scan->BlockSize - TotalSize;
  229.                                     }
  230.                                  else
  231.                                     {
  232.                                         FreeList = (BlockRec*)((char*)Scan + TotalSize);
  233.                                         /* pointer update first! (else for 0 length blocks, it will */
  234.                                         /* get overwritten) */
  235.                                         FreeList->Next = Scan->Next;
  236.                                         FreeList->BlockSize = Scan->BlockSize - TotalSize;
  237.                                     }
  238.                                 /* construct the header for the current block */
  239.                                 /* since we are splitting the block, we use the plopped off size */
  240.                                 /* so that both block's sizes add up to the original (see above) */
  241.                                 ERROR(DECODETOTALSIZE((unsigned long)SIZEENCODE(TotalSize,TotalSize
  242.                                     - RequestedBlockSize))
  243.                                     - DECODEREQUESTEDSIZE((unsigned long)SIZEENCODE(TotalSize,TotalSize
  244.                                     - RequestedBlockSize))
  245.                                     != TotalSize - RequestedBlockSize,PRERR(ForceAbort,
  246.                                     "BlockNew:  not enough bits specified for size correction"));
  247.                                 Scan->BlockSize = SIZEENCODE(TotalSize,TotalSize - RequestedBlockSize);
  248.                                 /* set up the return value */
  249.                                 ReturnValue = (void*)&(Scan->Next);
  250.                             }
  251.                         EXECUTE(CheckHeap());
  252.                         EXECUTE(AllocationCount += 1;)
  253.                         APRINT(("-BlockNew %r",ReturnValue));
  254.                         return ReturnValue;
  255.                     }
  256.                 /* this block wasn't big enough -- try the next one */
  257.                 Lag = Scan;
  258.                 Scan = Scan->Next;
  259.             }
  260.  
  261.         /* no memory on free list -- try to get more memory from system */
  262.         APRINT((" MoreCore"));
  263.         if (TotalSize > MINMORECORESIZE)
  264.             {
  265.                 /* no alignment worries since TotalSize is made to be aligned above */
  266.                 OSRequestedSize = TotalSize;
  267.             }
  268.          else
  269.             {
  270.                 OSRequestedSize = MINMORECORESIZE;
  271.             }
  272.  
  273.         /* first, try to grow the last handle we allocated */
  274.         if (LastHandleAllocated != NIL)
  275.             {
  276.                 long                OldHandleSize;
  277.  
  278.                 OldHandleSize = GetHandleSize(LastHandleAllocated);
  279.                 SetHandleSize(LastHandleAllocated,OldHandleSize + OSRequestedSize);
  280.                 if (MemError() == noErr)
  281.                     {
  282.                         /* set up the header for this new block, as if it were used */
  283.                         ERROR(DECODETOTALSIZE((unsigned long)SIZEENCODE(OSRequestedSize,
  284.                             sizeof(BlockRec))) - DECODEREQUESTEDSIZE((unsigned long)SIZEENCODE(
  285.                             OSRequestedSize,sizeof(BlockRec))) != sizeof(BlockRec),PRERR(ForceAbort,
  286.                             "BlockNew:  not enough bits specified for size correction"));
  287.                         (*(BlockRec*)(*(char**)LastHandleAllocated + OldHandleSize)).BlockSize
  288.                             = SIZEENCODE(OSRequestedSize,sizeof(BlockRec));
  289.                         /* release it with the normal block release routine */
  290.                         EXECUTE(AllocationCount += 1;) /* offset the effect of the following release */
  291.                         BlockRelease((void*)
  292.                             &((*(BlockRec*)(*(char**)LastHandleAllocated + OldHandleSize)).Next));
  293.                         /* try to allocate the block now */
  294.                         goto TryAgainPoint;
  295.                     }
  296.                  else
  297.                     {
  298.                         /* if the block couldn't be resized, then resize it up as large as */
  299.                         /* we can make it, if it's inside the application's heap.  this makes */
  300.                         /* sure we don't lose any space from the local heap. */
  301.                         if (LastHandleIsLocal)
  302.                             {
  303.                                 long                        AddedSpaceCounter;
  304.                                 OSErr                        ErrorThang;
  305.  
  306.                                 AddedSpaceCounter = 0;
  307.                                 do
  308.                                     {
  309.                                         SetHandleSize(LastHandleAllocated,GetHandleSize(LastHandleAllocated)
  310.                                             + LOCALINCREMENTSIZE);
  311.                                         ErrorThang = MemError();
  312.                                         if (ErrorThang == noErr)
  313.                                             {
  314.                                                 AddedSpaceCounter += LOCALINCREMENTSIZE;
  315.                                             }
  316.                                     } while (ErrorThang == noErr);
  317.                                 /* now incorporate new block, but only if there actually is one */
  318.                                 if (AddedSpaceCounter > 0)
  319.                                     {
  320.                                         /* set up the header for this new block, as if it were used */
  321.                                         ERROR(DECODETOTALSIZE((unsigned long)SIZEENCODE(AddedSpaceCounter,
  322.                                             sizeof(BlockRec))) - DECODEREQUESTEDSIZE(
  323.                                             (unsigned long)SIZEENCODE(AddedSpaceCounter,sizeof(BlockRec)))
  324.                                             != sizeof(BlockRec),PRERR(ForceAbort,"BlockNew:  not enough "
  325.                                             "bits specified for size correction"));
  326.                                         (*(BlockRec*)(*(char**)LastHandleAllocated + OldHandleSize))
  327.                                             .BlockSize = SIZEENCODE(AddedSpaceCounter,sizeof(BlockRec));
  328.                                         /* release it with the normal block release routine */
  329.                                         EXECUTE(AllocationCount += 1;) /* offset the effect of the release */
  330.                                         BlockRelease((void*)&((*(BlockRec*)(*(char**)LastHandleAllocated
  331.                                             + OldHandleSize)).Next));
  332.                                         /* prevent us from trying this little stunt again. */
  333.                                         LastHandleIsLocal = False;
  334.                                         /* try to allocate the block now */
  335.                                         goto TryAgainPoint;
  336.                                 }
  337.                             }
  338.                     }
  339.             }
  340.  
  341.         ERROR(MINEXOCORESIZE < MINMORECORESIZE,PRERR(ForceAbort,
  342.             "BlockNew:  MINEXOCORESIZE < MINMORECORESIZE"));
  343.         if (OSRequestedSize < LOWMEMRESERVEDSIZE + (32 * ALIGNMENT))
  344.             {
  345.                 /* keep from clobbering the reserved space that we so carefully set aside */
  346.                 OSRequestedSize = LOWMEMRESERVEDSIZE + (32 * ALIGNMENT);
  347.             }
  348.         ReserveMem(OSRequestedSize);
  349.         NewMemHandle = NewHandle(OSRequestedSize);
  350.         NewMemHandleIsLocal = True;
  351. #if !DEBUG  /* when debugging, we stay in local heap */
  352.         if (NewMemHandle == NIL)
  353.             {
  354.                 OSErr                Error;
  355.  
  356.                 if (OSRequestedSize < MINEXOCORESIZE)
  357.                     {
  358.                         /* if we allocate outside of our heap, we will grab larger */
  359.                         /* blocks of memory since we have less control over what may */
  360.                         /* get allocated on top of us.  hopefully this will reduce fragmentation */
  361.                         /* of blocks outside of the heap */
  362.                         OSRequestedSize = MINEXOCORESIZE;
  363.                     }
  364.                 NewMemHandle = TempNewHandle(OSRequestedSize,&Error);
  365.                 NewMemHandleIsLocal = False;
  366.             }
  367. #endif
  368.         if (NewMemHandle != NIL)
  369.             {
  370.                 ERROR((((unsigned long)(*NewMemHandle) & (ALIGNMENT - 1)) != 0),
  371.                     PRERR(ForceAbort,"Hey! The memory manager's block is not aligned!"));
  372.                 /* make sure the mac doesn't move our memory */
  373.                 HLock(NewMemHandle);
  374.                 /* set up the header for this new block, as if it were used */
  375.                 ERROR(DECODETOTALSIZE((unsigned long)SIZEENCODE(OSRequestedSize,
  376.                     sizeof(BlockRec))) - DECODEREQUESTEDSIZE((unsigned long)SIZEENCODE(
  377.                     OSRequestedSize,sizeof(BlockRec))) != sizeof(BlockRec),PRERR(ForceAbort,
  378.                     "BlockNew:  not enough bits specified for size correction"));
  379.                 (**(BlockRec**)NewMemHandle).BlockSize
  380.                     = SIZEENCODE(OSRequestedSize,sizeof(BlockRec));
  381.                 /* release it with the normal block release routine */
  382.                 EXECUTE(AllocationCount += 1;) /* offset the effect of the following release */
  383.                 BlockRelease((void*)&((**(BlockRec**)NewMemHandle).Next));
  384.                 /* remember the block's location so we can try to grow it later */
  385.                 LastHandleAllocated = NewMemHandle;
  386.                 LastHandleIsLocal = NewMemHandleIsLocal;
  387.                 /* try to allocate the block now */
  388.                 goto TryAgainPoint;
  389.             }
  390.         EXECUTE(CheckHeap());
  391.         APRINT(("-BlockNew NIL"));
  392.         return NIL;
  393.     }
  394.  
  395.  
  396. /* this routine releases blocks back to the free list and unifies them with */
  397. /* adjacent blocks if possible.  It keeps the free list in ascending */
  398. /* sorted order to make scanning for adjacent blocks more efficient. */
  399. void                BlockRelease(void* Block)
  400.     {
  401.         BlockRec*        Scan;
  402.         BlockRec*        Lag;
  403.         BlockRec*        OurNewBlock;
  404.  
  405.         /* this first thing is an ugly but portable way of treating *Block as the */
  406.         /* 'Next' field of a BlockRec and finding out where the BlockRec begins */
  407.         OurNewBlock = (BlockRec*)((char*)Block
  408.             - ((char*)&(((BlockRec*)NIL)->Next) - (char*)NIL));
  409.         APRINT(("+BlockRelease %r (size %l)",Block,DECODETOTALSIZE(OurNewBlock->BlockSize)));
  410.         /* adjust block's header so that it is free */
  411.         OurNewBlock->BlockSize = DECODETOTALSIZE(OurNewBlock->BlockSize);
  412.         /* scan the free list looking for items that can be appended / prepended to block */
  413.         Lag = NIL;
  414.         Scan = FreeList;
  415.         while ((Scan != NIL) && (Scan < OurNewBlock))
  416.             {
  417.                 /* searching so that Lag is before OurNewBlock and Scan is after it */
  418.                 Lag = Scan;
  419.                 Scan = Scan->Next;
  420.             }
  421.         /* now, scan is either NIL or the block right after this block. */
  422.         /* now trying to put block into the list */
  423.         OurNewBlock->Next = Scan;
  424.         if (Lag != NIL)
  425.             {
  426.                 Lag->Next = OurNewBlock;
  427.             }
  428.          else
  429.             {
  430.                 FreeList = OurNewBlock;
  431.             }
  432.         /* first, try to coalesce block with the one before it */
  433.         if (Lag != NIL)
  434.             {
  435.                 if ((char*)Lag + Lag->BlockSize == (char*)OurNewBlock)
  436.                     {
  437.                         /* if we can coalesce, dump block & add to Lag's size */
  438.                         Lag->BlockSize += OurNewBlock->BlockSize;
  439.                         Lag->Next = OurNewBlock->Next;
  440.                         OurNewBlock = Lag;
  441.                     }
  442.             }
  443.         /* now, try to coalesce block with the one after it */
  444.         if (Scan != NIL)
  445.             {
  446.                 if ((char*)OurNewBlock + OurNewBlock->BlockSize == (char*)Scan)
  447.                     {
  448.                         OurNewBlock->BlockSize += Scan->BlockSize;
  449.                         OurNewBlock->Next = Scan->Next;
  450.                     }
  451.             }
  452.         EXECUTE(CheckHeap());
  453.         EXECUTE(AllocationCount -= 1;)
  454.         APRINT(("-BlockRelease"));
  455.     }
  456.  
  457.  
  458. /* decode and return the size of an allocated block */
  459. long                BlockSize(void* Block)
  460.     {
  461.         BlockRec*        TrueBlockBase;
  462.  
  463.         TrueBlockBase = (BlockRec*)((char*)Block
  464.             - ((char*)&(((BlockRec*)NIL)->Next) - (char*)NIL));
  465.         APRINT(("*BlockSize %r %l",Block,DECODEREQUESTEDSIZE(TrueBlockBase->BlockSize)));
  466.         return DECODEREQUESTEDSIZE(TrueBlockBase->BlockSize);
  467.     }
  468.  
  469.  
  470. /* resize the block.  Minimum of the new size and the old size bytes of data will */
  471. /* be preserved.  NOTE:  The block's address may change!!! */
  472. /* this is a very naive implementation which simply allocates a new block, copies */
  473. /* over all the data, and releases the old block.  For performance reasons, this */
  474. /* might be improved to intelligently look for a free block adjacent to the */
  475. /* allocated block.  However, by always relocating the block, it is very good at */
  476. /* pointing out bugs that assume blocks don't move when resized. */
  477. void*                BlockResize(void* Block, long NewRequestedBlockSize)
  478.     {
  479.         void*                NewPointer;
  480.         long                ValidSectionSize;
  481.         long                OldBlockSize;
  482.  
  483.         APRINT(("+BlockResize %r",Block));
  484.         ERROR((NewRequestedBlockSize < 0) || (NewRequestedBlockSize > MEMBLOCKLIMIT),
  485.             PRERR(ForceAbort,"BlockSize:  allocation block size is out of range"));
  486.         NewPointer = BlockNew(NewRequestedBlockSize);
  487.         if (NewPointer == NIL)
  488.             {
  489.                 return NIL;
  490.             }
  491.         OldBlockSize = BlockSize(Block);
  492.         if (NewRequestedBlockSize < OldBlockSize)
  493.             {
  494.                 ValidSectionSize = NewRequestedBlockSize;
  495.             }
  496.          else
  497.             {
  498.                 ValidSectionSize = OldBlockSize;
  499.             }
  500.         CopyData((char*)Block,(char*)NewPointer,ValidSectionSize);
  501.         BlockRelease(Block);
  502.         APRINT(("-BlockResize %r",NewPointer));
  503.         return NewPointer;
  504.     }
  505.  
  506.  
  507. /* scan list and look for inconsistencies, such as overlapping blocks, blocks */
  508. /* outside of the range of the heap, misaligned blocks, and other weirdness */
  509. /* this routine only does something when debugging is enabled */
  510. #if DEBUG
  511. void                CheckHeap(void)
  512.     {
  513.         BlockRec*        Scan;
  514.         BlockRec*        Lag;
  515.         Zone*                Zone;
  516.         char*                ZoneBeginning;
  517.         char*                ZoneEnd;
  518.  
  519.         APRINT(("+CheckHeap"));
  520.         if (AllocationCount < 0)
  521.             {
  522.                 PRERR(ForceAbort,"CheckHeap:  Number of allocated blocks is negative");
  523.             }
  524.         Lag = NIL;
  525.         Scan = FreeList;
  526.         Zone = GetZone();
  527.         ZoneBeginning = (char*)Zone;
  528.         ZoneEnd = (char*)(Zone->bkLim);
  529.         while (Scan != NIL)
  530.             {
  531.                 if (((unsigned long)Scan & (ALIGNMENT - 1)) != 0)
  532.                     {
  533.                         PRERR(ForceAbort,"CheckHeap:  Bad link pointer encountered");
  534.                     }
  535.                 if ((Scan->BlockSize & (ALIGNMENT - 1)) != 0)
  536.                     {
  537.                         PRERR(ForceAbort,"CheckHeap:  Misaligned free block size value");
  538.                     }
  539.                 if (((char*)Scan < ZoneBeginning) || ((char*)Scan >= ZoneEnd))
  540.                     {
  541.                         PRERR(ForceAbort,"CheckHeap:  Reference is outside heap area!");
  542.                     }
  543.                 if (Scan->BlockSize < sizeof(BlockRec))
  544.                     {
  545.                         PRERR(ForceAbort,"CheckHeap:  Free block's size is too small");
  546.                     }
  547.                 if (Scan <= Lag)
  548.                     {
  549.                         PRERR(ForceAbort,"CheckHeap:  Block occurs lower than previous block");
  550.                     }
  551.                 if ((Scan->Next != NIL) && ((char*)Scan + Scan->BlockSize >= (char*)(Scan->Next)))
  552.                     {
  553.                         PRERR(ForceAbort,"CheckHeap:  Block + Size overshoots next block");
  554.                     }
  555.                 Scan = Scan->Next;
  556.             }
  557.         APRINT(("-CheckHeap"));
  558.     }
  559. #endif
  560.  
  561.  
  562. /* this routine dumps a file to the working directory containing a list of the */
  563. /* free blocks in the heap so that fragmentation performance can be analyzed */
  564. /* this routine only does something when debugging is enabled */
  565. #if DEBUG
  566. void                CheckFragmentation(void)
  567.     {
  568.         BlockRec*            Scan;
  569.         char                    DumpName[] = "\p!!HeapFragmentationDump";
  570.         static char        Hex[] = "0123456789abcdef";
  571.         short                    MemDumpFile;
  572.         char                    NumAllocatedBlocks[] = "Number of allocated blocks = xxxxxxxx\x0d";
  573.         long                    Index;
  574.  
  575.         FSDelete((unsigned char*)DumpName,0);
  576.         ERROR(Create((unsigned char*)DumpName,0,AUDITCREATOR,'TEXT') != noErr,
  577.             PRERR(ForceAbort,"Couldn't create fragmentation dump file"));
  578.         ERROR(FSOpen((unsigned char*)DumpName,0,&MemDumpFile) != noErr,PRERR(ForceAbort,
  579.             "Couldn't open fragmentation dump file for writing"));
  580.         for (Index = 0; Index < 8; Index += 1)
  581.             {
  582.                 NumAllocatedBlocks[29 + Index]
  583.                     = Hex[(AllocationCount >> ((7 - Index) * 4)) & 0x0f];
  584.             }
  585.         Index = 38;
  586.         FSWrite(MemDumpFile,&Index,NumAllocatedBlocks);
  587.         Scan = FreeList;
  588.         while (Scan != NIL)
  589.             {
  590.                 char                        Buffer[] = "xxxxxxxx..xxxxxxxx (xxxxxxxx)\x0d";
  591.                 long                        ByteCount = 30;
  592.                 unsigned long        Low;
  593.                 unsigned long        High;
  594.  
  595.                 Low = (unsigned long)Scan;
  596.                 High = Low + Scan->BlockSize - 1;
  597.                 for (Index = 0; Index < 8; Index += 1)
  598.                     {
  599.                         Buffer[Index] = Hex[(Low >> ((7 - Index) * 4)) & 0x0f];
  600.                     }
  601.                 for (Index = 0; Index < 8; Index += 1)
  602.                     {
  603.                         Buffer[Index + 10] = Hex[(High >> ((7 - Index) * 4)) & 0x0f];
  604.                     }
  605.                 for (Index = 0; Index < 8; Index += 1)
  606.                     {
  607.                         Buffer[Index + 20] = Hex[(Scan->BlockSize >> ((7 - Index) * 4)) & 0x0f];
  608.                     }
  609.                 FSWrite(MemDumpFile,&ByteCount,Buffer);
  610.                 Scan = Scan->Next;
  611.             }
  612.         FSClose(MemDumpFile);
  613.     }
  614. #endif
  615.